#include "dfs.h"
#include "dfs_inner.h"


/* 文件相关终端命令 */



#if (CONFIG_SHELL_ENABLE)

#include "shell.h"
#include "mem_sal.h"

static struct dfs_fd fd_obj;
static struct dirent dirent_obj;


static int list_fd(void)
{
    extern int dfs_list_fd(void);

    return dfs_list_fd();
}
// SHELL_CMD_EXPORT(fd, list file descriptor);
SHELL_CMD_EXPORT_ALIAS(list_fd, fd, list file descriptor);

/* file */


static int ls(int argc, char **argv)
{
    int len;
    char *path, *fullpath;
    struct stat fd_stat;

    if (argc == 1)
    {
        path = dfs_mem_strdup(dfs_get_working_dir());   // 当前打开的路径
        if (path == NULL)
        {
            printf("working dir null\r\n");
            return -1;
        }
    }
    else if (argc == 2)
    {
        path = argv[1];
    }
    else
    {
        printf("usage: ls [path]\r\n");
        return -1;
    }

    /* 列出当前目录 */
    if (dfs_file_open(&fd_obj, path, O_DIRECTORY) == 0)
    {

        printf("--- Path: %s\r\n", path);
        do
        {
            memset(&dirent_obj, 0, sizeof(struct dirent));
            len = dfs_file_getdents(&fd_obj, &dirent_obj, sizeof(struct dirent));
            if (len > 0)
            {                
                memset(&fd_stat, 0x00, sizeof(struct stat));
                
                fullpath = dfs_normalize_path(path, dirent_obj.d_name);
                if (fullpath == NULL) break;

                /**
                * --- Path: /
                * |- <DIR> -         flash
                * |- <DIR> -         sd
                * |- <DIR> -         udisk
                * |- <FIL> 35        readme.txt
                * --- end ---
                */                    
                printf("|- ");
                if (dfs_file_stat(fullpath, &fd_stat) == 0)
                {
                    if (S_ISDIR(fd_stat.st_mode))
                    {
                        printf("%-16s", "<DIR> -");
                    }
                    else
                    {
                        printf("<FIL> %-10lu", (unsigned long)fd_stat.st_size);
                    }
                    printf("%-20s", dirent_obj.d_name);
                }
                else
                {
                    printf("|- <BAD file>: %s", dirent_obj.d_name);
                    break;
                }
                printf("\r\n");

                dfs_mem_free(fullpath);
            }
        } while (len > 0);

        dfs_file_close(&fd_obj);
    }
    else
    {
        printf("No such dir\r\n");
    }

    if (argc == 1)
    {
        dfs_mem_free(path);
    }

    printf("--- end ---\r\n\r\n");

    return 0;
}
SHELL_CMD_EXPORT(ls, list directory contents);

static int rm(int argc, char **argv)
{
    char *filename;

    if (argc != 2)
    {
        printf("usage rm [dir/file]\r\n");
        return -1;
    }

    filename = argv[1];
    if (dfs_file_unlink(filename) < 0)
    {
        printf("delete %s fail!", filename);
    }

    return 0;
}
SHELL_CMD_EXPORT(rm, remove files or directories);

static int cat(int argc, char **argv)
{
    int len;
    char *filename;
    char buffer[81];

    if (argc != 2)
    {
        printf("usage rm [dir/file]\r\n");
        return -1;
    }

    filename = argv[1];
    if (dfs_file_open(&fd_obj, filename, O_RDONLY) < 0)
    {
        printf("open %s fail!\r\n", filename);
        return -1;
    }

    do
    {
        memset(buffer, 0, sizeof(buffer));
        len = dfs_file_read(&fd_obj, buffer, sizeof(buffer)-1);
        if (len > 0)
        {
            printf("%s", buffer);
        }
    } while (len > 0);
    printf("\r\n");
    dfs_file_close(&fd_obj);
    return 0;
}
SHELL_CMD_EXPORT(cat, print file);

#define BUF_SZ  4096
static void copyfile(const char *src, const char *dst)
{
    struct dfs_fd src_fd;
    uint8_t *block_ptr;
    uint32_t read_bytes;

    block_ptr = (uint8_t *)dfs_mem_malloc(BUF_SZ);
    if (block_ptr == NULL)
    {
        printf("out of memory\n");

        return;
    }

    if (dfs_file_open(&src_fd, src, O_RDONLY) < 0)
    {
        dfs_mem_free(block_ptr);
        printf("Read %s failed\n", src);

        return;
    }

    if (dfs_file_open(&fd_obj, dst, O_WRONLY | O_CREAT) < 0)
    {
        dfs_mem_free(block_ptr);
        dfs_file_close(&src_fd);

        printf("Write %s failed\n", dst);

        return;
    }

    do
    {
        read_bytes = dfs_file_read(&src_fd, block_ptr, BUF_SZ);
        if (read_bytes > 0)
        {
            int length;

            length = dfs_file_write(&fd_obj, block_ptr, read_bytes);
            if (length != read_bytes)
            {
                /* write failed. */
                printf("Write file data failed, errno=%d\n", length);
                break;
            }
        }
    }
    while (read_bytes > 0);

    dfs_file_close(&src_fd);
    dfs_file_close(&fd_obj);
    dfs_mem_free(block_ptr);
}

static int _mkdir(const char *path, mode_t mode)
{
    int fd;
    struct dfs_fd *d;
    int result;

    fd = dfs_fd_new();
    if (fd == -1)
    {
        // rt_set_errno(-ENOMEM);

        return -1;
    }

    d = dfs_fd_get(fd);

    result = dfs_file_open(d, path, O_DIRECTORY | O_CREAT);

    if (result < 0)
    {
        dfs_fd_put(d);
        dfs_fd_put(d);
        // rt_set_errno(result);

        return -1;
    }

    dfs_file_close(d);
    dfs_fd_put(d);
    dfs_fd_put(d);

    return 0;
}

static void copydir(const char *src, const char *dst)
{
    struct dirent dirent;
    struct stat stat;
    int length;
    struct dfs_fd cpfd;
    if (dfs_file_open(&cpfd, src, O_DIRECTORY) < 0)
    {
        printf("open %s failed\n", src);
        return ;
    }

    do
    {
        memset(&dirent, 0, sizeof(struct dirent));

        length = dfs_file_getdents(&cpfd, &dirent, sizeof(struct dirent));
        if (length > 0)
        {
            char *src_entry_full = NULL;
            char *dst_entry_full = NULL;

            if (strcmp(dirent.d_name, "..") == 0 || strcmp(dirent.d_name, ".") == 0)
                continue;

            /* build full path for each file */
            if ((src_entry_full = dfs_normalize_path(src, dirent.d_name)) == NULL)
            {
                printf("out of memory!\n");
                break;
            }
            if ((dst_entry_full = dfs_normalize_path(dst, dirent.d_name)) == NULL)
            {
                printf("out of memory!\n");
                dfs_mem_free(src_entry_full);
                break;
            }

            memset(&stat, 0, sizeof(struct stat));
            if (dfs_file_stat(src_entry_full, &stat) != 0)
            {
                printf("open file: %s failed\n", dirent.d_name);
                continue;
            }

            if (S_ISDIR(stat.st_mode))
            {
                _mkdir(dst_entry_full, 0);
                copydir(src_entry_full, dst_entry_full);
            }
            else
            {
                copyfile(src_entry_full, dst_entry_full);
            }
            dfs_mem_free(src_entry_full);
            dfs_mem_free(dst_entry_full);
        }
    }
    while (length > 0);

    dfs_file_close(&cpfd);
}

static const char *_get_path_lastname(const char *path)
{
    char *ptr;
    if ((ptr = (char *)strrchr(path, '/')) == NULL)
        return path;

    /* skip the '/' then return */
    return ++ptr;
}

static int cp(int argc, char **argv)
{
#define FLAG_SRC_TYPE      0x03
#define FLAG_SRC_IS_DIR    0x01
#define FLAG_SRC_IS_FILE   0x02
#define FLAG_SRC_NON_EXSIT 0x00

#define FLAG_DST_TYPE      0x0C
#define FLAG_DST_IS_DIR    0x04
#define FLAG_DST_IS_FILE   0x08
#define FLAG_DST_NON_EXSIT 0x00

    const char *src, *dst;
    struct stat stat;
    uint32_t flag = 0;

    if (argc != 3)
    {
        printf("usage: cp [src] [dst]");
        return -1;
    }
    src = argv[1];
    dst = argv[2];

    /* check the staus of src and dst */
    if (dfs_file_stat(src, &stat) < 0)
    {
        printf("copy failed, bad %s\n", src);
        return -1;
    }
    if (S_ISDIR(stat.st_mode)) flag |= FLAG_SRC_IS_DIR;
    else flag |= FLAG_SRC_IS_FILE;

    if (dfs_file_stat(dst, &stat) < 0)
    {
        flag |= FLAG_DST_NON_EXSIT;
    }
    else
    {
        if (S_ISDIR(stat.st_mode)) flag |= FLAG_DST_IS_DIR;
        else flag |= FLAG_DST_IS_FILE;
    }

    //2. check status
    if ((flag & FLAG_SRC_IS_DIR) && (flag & FLAG_DST_IS_FILE))
    {
        printf("cp faild, cp dir to file is not permitted!\n");
        return -1;
    }

    //3. do copy
    if (flag & FLAG_SRC_IS_FILE)
    {
        if (flag & FLAG_DST_IS_DIR)
        {
            char *fdst;
            fdst = dfs_normalize_path(dst, _get_path_lastname(src));
            if (fdst == NULL)
            {
                printf("out of memory\n");
                return -1;
            }
            copyfile(src, fdst);
            dfs_mem_free(fdst);
        }
        else
        {
            copyfile(src, dst);
        }
    }
    else //flag & FLAG_SRC_IS_DIR
    {
        if (flag & FLAG_DST_IS_DIR)
        {
            char *fdst;
            fdst = dfs_normalize_path(dst, _get_path_lastname(src));
            if (fdst == NULL)
            {
                printf("out of memory\n");
                return -1;
            }
            _mkdir(fdst, 0);
            copydir(src, fdst);
            dfs_mem_free(fdst);
        }
        else if ((flag & FLAG_DST_TYPE) == FLAG_DST_NON_EXSIT)
        {
            _mkdir(dst, 0);
            copydir(src, dst);
        }
        else
        {
            copydir(src, dst);
        }
    }
    return 0;
}
SHELL_CMD_EXPORT(cp, copy file or dir)




#endif // CONFIG_SHELL_ENABLE










